
namespace Patterns4Net.Core.AttributesPatterns
{
    using System.Diagnostics.Contracts;
    using System.Linq;
    using Mono.Cecil;
    using Attributes.DesignPatterns;
    using Patterns;
    using Attributes;

    /// <summary>
    /// This implementation of <see cref="ICreatePatternFromAttribute"/> implements a 
    /// template method for creation of so called 'wrapper' patterns <see cref="Adapter"/>, 
    /// <see cref="Proxy"/>, <see cref="Decorator"/> and <see cref="Bridge"/>.
    /// </summary>
    /// <remarks>
    /// <para>These patterns are expected to have a constructor with three parameters of 
    /// types: TypeDefinition, TypeReference and TypeReference, which represents 
    /// the target class on which the attribute was applied; the target interface 
    /// which this class implements (e.g., Target role in case of the Adapter pattern); 
    /// and the wrapped type.</para>
    /// </remarks>
    /// <typeparam name="TWrapper">The pattern type.</typeparam>
    public abstract class WrapperCreator<TWrapper> : ICreatePatternFromAttribute 
        where TWrapper : IPattern, IWrapper
    {
        [TemplateMethod]
        public IPattern Crate(CustomAttribute attribute, IMemberDefinition targetType)
        {
            var target = targetType as TypeDefinition;
            TypeReference targetInterface = null;
            TypeReference wrappedInterface = null;

            if (attribute.HasProperties)
            {
                targetInterface = GetProperty(attribute, this.TargetInterfacePropertyName) as TypeReference;
                wrappedInterface = GetProperty(attribute, this.WrappedTypePropertyName) as TypeReference;
            }

            var ctor = typeof (TWrapper).GetConstructor(new[]
                                                 {
                                                     typeof (TypeDefinition), 
                                                     typeof (TypeReference),
                                                     typeof (TypeReference)
                                                 });
            Contract.Assert(
                ctor != null,
                string.Format(
                    "WrapperCreator: the type {0} should have a constructor with 3 parameters.",
                    typeof (TWrapper)));
            var result = ctor.Invoke(new object[] { target, targetInterface, wrappedInterface });
            return (TWrapper)result;
        }

        protected abstract string TargetInterfacePropertyName { get; }

        protected abstract string WrappedTypePropertyName { get; }

        private static object GetProperty(CustomAttribute attribute, string name)
        {
            if (attribute.Properties.Any(p => string.CompareOrdinal(p.Name, name) == 0))
            {
                return attribute.Properties.First(p => string.CompareOrdinal(p.Name, name) == 0).Argument.Value;
            }

            return null;
        }
    }

    [PatternCreatorExport(typeof(AdapterAttribute))]
    public class AdapterCreator : WrapperCreator<Adapter>
    {
        protected override string TargetInterfacePropertyName
        {
            get { return "Target"; }
        }

        protected override string WrappedTypePropertyName
        {
            get { return "Adaptee"; }
        }
    }

    [PatternCreatorExport(typeof(DecoratorAttribute))]
    public class DecoratorCreator : WrapperCreator<Decorator>
    {
        protected override string TargetInterfacePropertyName
        {
            get { return "Decorated"; }
        }

        protected override string WrappedTypePropertyName
        {
            get { return "Decorated"; }
        }
    }

    [PatternCreatorExport(typeof(ProxyAttribute))]
    public class ProxyCreator : WrapperCreator<Proxy>
    {
        protected override string TargetInterfacePropertyName
        {
            get { return "Subject"; }
        }

        protected override string WrappedTypePropertyName
        {
            get { return "RealSubject"; }
        }
    }

    [PatternCreatorExport(typeof(BridgeAttribute))]
    public class BridgeCreator : WrapperCreator<Bridge>
    {
        protected override string TargetInterfacePropertyName
        {
            get { return "Implementor"; }
        }

        protected override string WrappedTypePropertyName
        {
            get { return "WrappedType"; }
        }
    }
}
